#include "lcd_driver.h"
#include "stdio.h"
#define write_rs(x) 		gpio_write(GPIOA,SOC_LCD_RS_GPIO_MSK,x)
#define write_rw(x) 		gpio_write(GPIOA,SOC_LCD_RW_GPIO_MSK,x)
#define write_b4(x) 		gpio_write(GPIOA,SOC_LCD_DB4_GPIO_MSK,x)
#define write_b5(x) 		gpio_write(GPIOA,SOC_LCD_DB5_GPIO_MSK,x)
#define write_b6(x) 		gpio_write(GPIOA,SOC_LCD_DB6_GPIO_MSK,x)
#define write_b7(x) 		gpio_write(GPIOA,SOC_LCD_DB7_GPIO_MSK,x)
#define write_e(x) 			gpio_write(GPIOA,SOC_LCD_E_GPIO_MSK,x)


void delay_forloop(int x){
	for(int i=0;i<2*x;i++);	// every 2 loop approximately take 16 clk, ~1us
	return;
}

void lcd_4b_interface(uint32_t rs, uint32_t rw, uint32_t data_bits){
	write_e(0);
	write_rs(rs);
	write_rw(rw);
	write_b4(data_bits&0x1);
	write_b5(data_bits&0x2);
	write_b6(data_bits&0x4);
	write_b7(data_bits&0x8);
	delay_forloop(1);
	write_e(1);
	delay_forloop(10);
	write_e(0);
	return;
}

void lcd_8b_interface(uint32_t rs, uint32_t rw, uint32_t data_bits){
	write_e(0);
	write_rs(rs);
	write_rw(rw);
	write_b4(data_bits&0x10);
	write_b5(data_bits&0x20);
	write_b6(data_bits&0x40);
	write_b7(data_bits&0x80);
	delay_forloop(1);
	write_e(1);
	delay_forloop(1);   	// delay more than 230ns
	write_e(0);
	delay_forloop(10); 		// delay more than 1us
	write_b4(data_bits&0x01);
	write_b5(data_bits&0x02);
	write_b6(data_bits&0x04);
	write_b7(data_bits&0x08);
	delay_forloop(1);
	write_e(1);
	delay_forloop(1);   	// delay more than 230ns
	write_e(0);
	delay_forloop(50);	// delay more than 40us

	return;
}

void lcd_init(){

	// GPIO initialization
	gpio_enable_output(GPIOA, SOC_LCD_CTRL_GPIO_MSK | SOC_LCD_DB_GPIO_MSK);
	gpio_write(GPIOA, SOC_LCD_CTRL_GPIO_MSK | SOC_LCD_DB_GPIO_MSK, GPIO_BIT_ALL_ZERO);
	delay_1ms(15); 			// delay more than 15ms

	// Power on initialization
	lcd_4b_interface(0, 0, 0x3);
	delay_1ms(5);			// delay more than 4.1ms
	lcd_4b_interface(0, 0, 0x3);
	delay_1ms(1);			// delay more than 100us
	lcd_4b_interface(0, 0, 0x3);
	delay_forloop(50);	// delay more than 40us
	lcd_4b_interface(0, 0, 0x2);
	delay_forloop(50);	// delay more than 40us
	delay_1ms(1);

	// Display configure
	lcd_8b_interface(0, 0, 0x28);	//function set
	delay_1ms(1);
	lcd_8b_interface(0, 0, 0x06);	//entry mode set
	delay_1ms(1);
	lcd_8b_interface(0, 0, 0x0c);	//display on and hide cursor
	delay_1ms(1);
	lcd_8b_interface(0, 0, 0x01);	//clear display
	delay_1ms(2);

	return;
}

void lcd_clear(){
	lcd_8b_interface(0, 0, 0x01);	//clear display
	delay_1ms(2);
	return;
}

void lcd_print(int len, char str[],int row, int col){
	int addr = row ? col + 0x40 : col;
	lcd_8b_interface(0, 0, 0x80|addr);	//set display address
	for(int i=0;i<len;i++){
		lcd_8b_interface(1, 0, str[i]);
	}
	return;
}
